{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# Using GLM-4 and setting up multi role division of labor to complete complex tasks\n",
    "\n",
    "**This tutorial is Only in Chinese explanation**\n",
    "\n",
    "本代码建立了一个简单的客服提示词场景，通过不同的提示词，使得模型具备了不同子场景内进行专业回复的能力。\n",
    "本代码旨在展示如何使用 GLM-4 模型完成：\n",
    "1. 具体场景的子任务提示词调度思维。\n",
    "2. 不同场景之间大模型的意图识别和切换。\n",
    "3. 多角色分工的场景设计思维。\n",
    "4. 通过多个角色，使用完全相同的基座模型来完成更加复杂的任务。\n",
    "\n",
    "请注意，本仓库不涉及 Agent 中的 Function Call 环节和 Plan 环节，仅设计到简单的角色切换。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "6072f8ed2122c0ac"
  },
  {
   "cell_type": "markdown",
   "source": [
    "首先，将 API Key 信息填入系统环境中。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "fa37c1d363ca9082"
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "outputs": [],
   "source": [
    "import os\n",
    "from zhipuai import ZhipuAI\n",
    "\n",
    "os.environ[\"ZHIPUAI_API_KEY\"] = \"your api key\"\n",
    "client = ZhipuAI()"
   ],
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2024-01-29T08:35:18.151132Z",
     "start_time": "2024-01-29T08:35:17.956957Z"
    }
   },
   "id": "initial_id"
  },
  {
   "cell_type": "markdown",
   "source": [
    "我为当前场景设定了四个角色，分别是：\n",
    "1. 通用客服，这个客服将简单的识别用户的问题并分配给专业的工作人员来完成\n",
    "2. 注册工作人员，这个工作人员了解如何处理用户的注册问题，包括需要向用户获取什么信息，会给用户返回注册结果。\n",
    "3. 注销工作人员，这个工作人员了解如何处理用户的注销问题，包括需要向用户获取什么信息，并且告诉用户结果是如何提供的\n",
    "4. 查询工作人员，这个工作人员了解如何处理用户的查询问题，包括需要向用户获取正确的查询内容并返回查询结果。\n",
    "\n",
    "在这里，我们为场景设计好了提示词。\n",
    "\n",
    "由于 GLM-4 具有较强的指令跟随能力，我在意图识别中加入了特殊字段，通过特殊字段的匹配来切换不同的角色，在实际工程中，情况远远比这个复杂。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "3063d55736077b71"
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "outputs": [],
   "source": [
    "sys_prompt = \"\"\"你是一个聪明的客服。您将能够根据用户的问题将不同的任务分配给不同的人。您有以下业务线：\n",
    "1.用户注册。如果用户想要执行这样的操作，您应该发送一个带有\"registered workers\"的特殊令牌。并告诉用户您正在调用它。\n",
    "2.用户数据查询。如果用户想要执行这样的操作，您应该发送一个带有\"query workers\"的特殊令牌。并告诉用户您正在调用它。\n",
    "3.删除用户数据。如果用户想执行这种类型的操作，您应该发送一个带有\"delete workers\"的特殊令牌。并告诉用户您正在调用它。\n",
    "\"\"\"\n",
    "registered_prompt = \"\"\"\n",
    "您的任务是根据用户信息存储数据。您需要从用户那里获得以下信息：\n",
    "1.用户名、性别、年龄\n",
    "2.用户设置的密码\n",
    "3.用户的电子邮件地址\n",
    "如果用户没有提供此信息，您需要提示用户提供。如果用户提供了此信息，则需要将此信息存储在数据库中，并告诉用户注册成功。\n",
    "存储方法是使用SQL语句。您可以使用SQL编写插入语句，并且需要生成用户ID并将其返回给用户。\n",
    "如果用户没有新问题，您应该回复带有 \"customer service\" 的特殊令牌，以结束任务。\n",
    "\"\"\"\n",
    "query_prompt = \"\"\"\n",
    "您的任务是查询用户信息。您需要从用户那里获得以下信息：\n",
    "1.用户ID\n",
    "2.用户设置的密码\n",
    "如果用户没有提供此信息，则需要提示用户提供。如果用户提供了此信息，那么需要查询数据库。如果用户ID和密码匹配，则需要返回用户的信息。\n",
    "如果用户没有新问题，您应该回复带有 \"customer service\" 的特殊令牌，以结束任务。\n",
    "\"\"\"\n",
    "delete_prompt = \"\"\"\n",
    "您的任务是删除用户信息。您需要从用户那里获得以下信息：\n",
    "1.用户ID\n",
    "2.用户设置的密码\n",
    "3.用户的电子邮件地址\n",
    "如果用户没有提供此信息，则需要提示用户提供该信息。\n",
    "如果用户提供了这些信息，则需要查询数据库。如果用户ID和密码匹配，您需要通知用户验证码已发送到他们的电子邮件，需要进行验证。\n",
    "如果用户没有新问题，您应该回复带有 \"customer service\" 的特殊令牌，以结束任务。\n",
    "\"\"\""
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-29T08:35:18.155448Z",
     "start_time": "2024-01-29T08:35:18.153196Z"
    }
   },
   "id": "a116549a044e736f"
  },
  {
   "cell_type": "markdown",
   "source": [
    "接着，我们设计了一个简单的类，这个类有以下功能\n",
    "1. 我们将每个不同的角色历史分离，这些历史之间不会相互交互，但是会在需要的时候将用户的请求或者模型的回答传入到另一个历史中。\n",
    "2. 实现简单的连续对话，并在作为客服的时候加入意图识别（提示词设定），在专业工作人员的情况下进行简单的多轮对话。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "3b7a3f07817d87b8"
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "outputs": [],
   "source": [
    "class SmartAssistant:\n",
    "    def __init__(self):\n",
    "        self.client = ZhipuAI()\n",
    "\n",
    "        self.system_prompt = sys_prompt\n",
    "        self.registered_prompt = registered_prompt\n",
    "        self.query_prompt = query_prompt\n",
    "        self.delete_prompt = delete_prompt\n",
    "\n",
    "        # Using a dictionary to store different sets of messages\n",
    "        self.messages = {\n",
    "            \"system\": [{\"role\": \"system\", \"content\": self.system_prompt}],\n",
    "            \"registered\": [{\"role\": \"system\", \"content\": self.registered_prompt}],\n",
    "            \"query\": [{\"role\": \"system\", \"content\": self.query_prompt}],\n",
    "            \"delete\": [{\"role\": \"system\", \"content\": self.delete_prompt}]\n",
    "        }\n",
    "\n",
    "        # Current assignment for handling messages\n",
    "        self.current_assignment = \"system\"\n",
    "\n",
    "    def get_response(self, user_input):\n",
    "        self.messages[self.current_assignment].append({\"role\": \"user\", \"content\": user_input})\n",
    "        while True:\n",
    "            response = self.client.chat.completions.create(\n",
    "                model=\"glm-4\",\n",
    "                messages=self.messages[self.current_assignment],\n",
    "                temperature=0.9,\n",
    "                stream=False,\n",
    "                max_tokens=2000,\n",
    "            )\n",
    "\n",
    "            ai_response = response.choices[0].message.content\n",
    "            if \"registered workers\" in ai_response:\n",
    "                self.current_assignment = \"registered\"\n",
    "                print(\"意图识别:\",ai_response)\n",
    "                print(\"switch to <registered>\")\n",
    "                self.messages[self.current_assignment].append({\"role\": \"user\", \"content\": user_input})\n",
    "            elif \"query workers\" in ai_response:\n",
    "                self.current_assignment = \"query\"\n",
    "                print(\"意图识别:\",ai_response)\n",
    "                print(\"switch to <query>\")\n",
    "                self.messages[self.current_assignment].append({\"role\": \"user\", \"content\": user_input})\n",
    "            elif \"delete workers\" in ai_response:\n",
    "                self.current_assignment = \"delete\"\n",
    "                print(\"意图识别:\",ai_response)\n",
    "                print(\"switch to <delete>\")\n",
    "                self.messages[self.current_assignment].append({\"role\": \"user\", \"content\": user_input})\n",
    "            elif \"customer service\" in ai_response:\n",
    "                print(\"意图识别:\",ai_response)\n",
    "                print(\"switch to <customer service>\")\n",
    "                self.messages[\"system\"] += self.messages[self.current_assignment]\n",
    "                self.current_assignment = \"system\"\n",
    "                return ai_response\n",
    "            else:\n",
    "                self.messages[self.current_assignment].append({\"role\": \"assistant\", \"content\": ai_response})\n",
    "                return ai_response\n",
    "\n",
    "    def start_conversation(self):\n",
    "        while True:\n",
    "            user_input = input(\"User: \")\n",
    "            if user_input.lower() in ['exit', 'quit']:\n",
    "                print(\"Exiting conversation.\")\n",
    "                break\n",
    "            response = self.get_response(user_input)\n",
    "            print(\"Assistant:\", response)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-29T08:35:18.161078Z",
     "start_time": "2024-01-29T08:35:18.159279Z"
    }
   },
   "id": "99cff1913703268f"
  },
  {
   "cell_type": "markdown",
   "source": [
    "接着，我们就可以开始连续对话了，模型会打印模型的思考和回复过程，并且会打印出是否切换了角色。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "bdb6bcdb1002993e"
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "意图识别: 好的，我现在为您调用\"registered workers\"。请稍等，我将发送一个特殊令牌以完成您的注册请求。\n",
      "switch to <registered>\n",
      "Assistant: 好的，欢迎您注册账号！请提供以下信息：\n",
      "\n",
      "1. 用户名\n",
      "2. 性别\n",
      "3. 年龄\n",
      "4. 设置密码\n",
      "5. 电子邮件地址\n",
      "\n",
      "请按顺序告诉我这些信息。如果您有任何疑问或需要帮助，请随时告诉我。\n",
      "Assistant: 非常好，zR，您提供的信息如下：\n",
      "\n",
      "- 用户名：zR\n",
      "- 性别：女\n",
      "- 年龄：19岁\n",
      "\n",
      "现在，请设置您的密码，并提供您的电子邮件地址。\n",
      "Assistant: 好的，我已经收到以下信息：\n",
      "\n",
      "- 用户名：zR\n",
      "- 性别：女\n",
      "- 年龄：19岁\n",
      "- 电子邮件：123456@gmail.com\n",
      "- 密码：123456\n",
      "\n",
      "现在我将使用SQL语句将这些信息存储到数据库中，并生成一个用户ID。\n",
      "\n",
      "以下是SQL插入语句的示例：\n",
      "\n",
      "```sql\n",
      "INSERT INTO users (username, gender, age, email, password) \n",
      "VALUES ('zR', 'female', 19, '123456@gmail.com', '123456');\n",
      "```\n",
      "\n",
      "假设数据库自动生成用户ID，我们可以这样获取：\n",
      "\n",
      "```sql\n",
      "SELECT LAST_INSERT_ID();\n",
      "```\n",
      "\n",
      "假设这个语句返回的用户ID是123456，那么：\n",
      "\n",
      "您已成功注册！您的用户ID是：123456。\n",
      "\n",
      "请注意，上面的密码是以明文形式存储的，这在实际应用中是不安全的。在实际系统中，应该使用加密函数来存储密码的哈希值。\n",
      "\n",
      "如果没有什么问题，我们的对话可以结束了。如果有其他需要帮助的地方，请随时告诉我。\n",
      "意图识别: 不客气！如果您需要任何帮助，请随时回来。祝您使用愉快！\n",
      "\n",
      "由于您没有新的问题，我将使用特殊令牌 \"customer service\" 结束本次任务。\n",
      "\n",
      "如果您需要进一步的帮助，请随时联系我们，我们的客户服务团队随时为您服务。再见！\n",
      "switch to <customer service>\n",
      "Assistant: 不客气！如果您需要任何帮助，请随时回来。祝您使用愉快！\n",
      "\n",
      "由于您没有新的问题，我将使用特殊令牌 \"customer service\" 结束本次任务。\n",
      "\n",
      "如果您需要进一步的帮助，请随时联系我们，我们的客户服务团队随时为您服务。再见！\n"
     ]
    },
    {
     "ename": "APIRequestFailedError",
     "evalue": "Error code: 400, with error text {\"error\":{\"code\":\"1214\",\"message\":\"messages[10]:content和tool_calls 字段不能同时为空\"}}",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mAPIRequestFailedError\u001B[0m                     Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[4], line 2\u001B[0m\n\u001B[1;32m      1\u001B[0m assistant \u001B[38;5;241m=\u001B[39m SmartAssistant()\n\u001B[0;32m----> 2\u001B[0m \u001B[43massistant\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mstart_conversation\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n",
      "Cell \u001B[0;32mIn[3], line 64\u001B[0m, in \u001B[0;36mSmartAssistant.start_conversation\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m     62\u001B[0m     \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mExiting conversation.\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m     63\u001B[0m     \u001B[38;5;28;01mbreak\u001B[39;00m\n\u001B[0;32m---> 64\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_response\u001B[49m\u001B[43m(\u001B[49m\u001B[43muser_input\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m     65\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mAssistant:\u001B[39m\u001B[38;5;124m\"\u001B[39m, response)\n",
      "Cell \u001B[0;32mIn[3], line 24\u001B[0m, in \u001B[0;36mSmartAssistant.get_response\u001B[0;34m(self, user_input)\u001B[0m\n\u001B[1;32m     22\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mmessages[\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mcurrent_assignment]\u001B[38;5;241m.\u001B[39mappend({\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mrole\u001B[39m\u001B[38;5;124m\"\u001B[39m: \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124muser\u001B[39m\u001B[38;5;124m\"\u001B[39m, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mcontent\u001B[39m\u001B[38;5;124m\"\u001B[39m: user_input})\n\u001B[1;32m     23\u001B[0m \u001B[38;5;28;01mwhile\u001B[39;00m \u001B[38;5;28;01mTrue\u001B[39;00m:\n\u001B[0;32m---> 24\u001B[0m     response \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mclient\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mchat\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcompletions\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcreate\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m     25\u001B[0m \u001B[43m        \u001B[49m\u001B[43mmodel\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mglm-4\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m     26\u001B[0m \u001B[43m        \u001B[49m\u001B[43mmessages\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mmessages\u001B[49m\u001B[43m[\u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcurrent_assignment\u001B[49m\u001B[43m]\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     27\u001B[0m \u001B[43m        \u001B[49m\u001B[43mtemperature\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;241;43m0.9\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m     28\u001B[0m \u001B[43m        \u001B[49m\u001B[43mstream\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m     29\u001B[0m \u001B[43m        \u001B[49m\u001B[43mmax_tokens\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;241;43m2000\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m     30\u001B[0m \u001B[43m    \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m     32\u001B[0m     ai_response \u001B[38;5;241m=\u001B[39m response\u001B[38;5;241m.\u001B[39mchoices[\u001B[38;5;241m0\u001B[39m]\u001B[38;5;241m.\u001B[39mmessage\u001B[38;5;241m.\u001B[39mcontent\n\u001B[1;32m     33\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mregistered workers\u001B[39m\u001B[38;5;124m\"\u001B[39m \u001B[38;5;129;01min\u001B[39;00m ai_response:\n",
      "File \u001B[0;32m~/Code/glm-cookbook/venv/lib/python3.9/site-packages/zhipuai/api_resource/chat/completions.py:48\u001B[0m, in \u001B[0;36mCompletions.create\u001B[0;34m(self, model, request_id, do_sample, stream, temperature, top_p, max_tokens, seed, messages, stop, sensitive_word_check, tools, tool_choice, extra_headers, disable_strict_validation, timeout)\u001B[0m\n\u001B[1;32m     46\u001B[0m     _cast_type \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mobject\u001B[39m\n\u001B[1;32m     47\u001B[0m     _stream_cls \u001B[38;5;241m=\u001B[39m StreamResponse[\u001B[38;5;28mobject\u001B[39m]\n\u001B[0;32m---> 48\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_post\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m     49\u001B[0m \u001B[43m    \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43m/chat/completions\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m     50\u001B[0m \u001B[43m    \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m{\u001B[49m\n\u001B[1;32m     51\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mmodel\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mmodel\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     52\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mrequest_id\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mrequest_id\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     53\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mtemperature\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mtemperature\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     54\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mtop_p\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mtop_p\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     55\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mdo_sample\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mdo_sample\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     56\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mmax_tokens\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mmax_tokens\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     57\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mseed\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mseed\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     58\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mmessages\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mmessages\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     59\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mstop\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mstop\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     60\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43msensitive_word_check\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43msensitive_word_check\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     61\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mstream\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mstream\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     62\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mtools\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mtools\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     63\u001B[0m \u001B[43m        \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mtool_choice\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43mtool_choice\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     64\u001B[0m \u001B[43m    \u001B[49m\u001B[43m}\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     65\u001B[0m \u001B[43m    \u001B[49m\u001B[43moptions\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mmake_user_request_input\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m     66\u001B[0m \u001B[43m        \u001B[49m\u001B[43mextra_headers\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mextra_headers\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     67\u001B[0m \u001B[43m    \u001B[49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     68\u001B[0m \u001B[43m    \u001B[49m\u001B[43mcast_type\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_cast_type\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     69\u001B[0m \u001B[43m    \u001B[49m\u001B[43menable_stream\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mstream\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;129;43;01mor\u001B[39;49;00m\u001B[43m \u001B[49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m     70\u001B[0m \u001B[43m    \u001B[49m\u001B[43mstream_cls\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_stream_cls\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m     71\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[0;32m~/Code/glm-cookbook/venv/lib/python3.9/site-packages/zhipuai/core/_http_client.py:292\u001B[0m, in \u001B[0;36mHttpClient.post\u001B[0;34m(self, path, body, cast_type, options, files, enable_stream, stream_cls)\u001B[0m\n\u001B[1;32m    278\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mpost\u001B[39m(\n\u001B[1;32m    279\u001B[0m         \u001B[38;5;28mself\u001B[39m,\n\u001B[1;32m    280\u001B[0m         path: \u001B[38;5;28mstr\u001B[39m,\n\u001B[0;32m   (...)\u001B[0m\n\u001B[1;32m    287\u001B[0m         stream_cls: \u001B[38;5;28mtype\u001B[39m[StreamResponse[Any]] \u001B[38;5;241m|\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m,\n\u001B[1;32m    288\u001B[0m ) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m ResponseT \u001B[38;5;241m|\u001B[39m StreamResponse:\n\u001B[1;32m    289\u001B[0m     opts \u001B[38;5;241m=\u001B[39m ClientRequestParam\u001B[38;5;241m.\u001B[39mconstruct(method\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mpost\u001B[39m\u001B[38;5;124m\"\u001B[39m, json_data\u001B[38;5;241m=\u001B[39mbody, files\u001B[38;5;241m=\u001B[39mmake_httpx_files(files), url\u001B[38;5;241m=\u001B[39mpath,\n\u001B[1;32m    290\u001B[0m                                         \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39moptions)\n\u001B[0;32m--> 292\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m    293\u001B[0m \u001B[43m        \u001B[49m\u001B[43mcast_type\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcast_type\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mparams\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mopts\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m    294\u001B[0m \u001B[43m        \u001B[49m\u001B[43menable_stream\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43menable_stream\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m    295\u001B[0m \u001B[43m        \u001B[49m\u001B[43mstream_cls\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mstream_cls\u001B[49m\n\u001B[1;32m    296\u001B[0m \u001B[43m    \u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[0;32m~/Code/glm-cookbook/venv/lib/python3.9/site-packages/zhipuai/core/_http_client.py:251\u001B[0m, in \u001B[0;36mHttpClient.request\u001B[0;34m(self, cast_type, params, enable_stream, stream_cls)\u001B[0m\n\u001B[1;32m    249\u001B[0m     err\u001B[38;5;241m.\u001B[39mresponse\u001B[38;5;241m.\u001B[39mread()\n\u001B[1;32m    250\u001B[0m     \u001B[38;5;66;03m# raise err\u001B[39;00m\n\u001B[0;32m--> 251\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_make_status_error(err\u001B[38;5;241m.\u001B[39mresponse) \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[1;32m    253\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m err:\n\u001B[1;32m    254\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m err\n",
      "\u001B[0;31mAPIRequestFailedError\u001B[0m: Error code: 400, with error text {\"error\":{\"code\":\"1214\",\"message\":\"messages[10]:content和tool_calls 字段不能同时为空\"}}"
     ]
    }
   ],
   "source": [
    "assistant = SmartAssistant()\n",
    "assistant.start_conversation()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-29T08:36:14.446970Z",
     "start_time": "2024-01-29T08:35:18.161455Z"
    }
   },
   "id": "80491bbd1c581679"
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 提醒\n",
    "\n",
    "在这里，我只是使用了最简单的切换方式来切换角色，实际工程中，你应该关注这些问题：\n",
    "1. 切换角色中的工程构建\n",
    "2. 历史的管理\n",
    "3. 本代码没有设计到工具调用，否则，你还需要设计不同角色对应的工具和Function Call功能。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "595d8b4ec63eb9d1"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
